using System;
using System.Data.SqlClient;
using System.Reflection;
using System.Runtime.Serialization;

namespace EntLibExtensions.Tests
{
    /// <summary>
    /// Modified from sample at http://blogs.msdn.com/rido/archive/2006/11/26/mocking-sqlexception.aspx
    /// </summary>
    public class SqlExceptionCreator
    {

        public static SqlException CreateSqlException(string mainMessage, params Error[] errors)
        {
            SqlErrorCollection collection = GetErrorCollection();

            MethodInfo addMethod = collection.GetType().GetMethod("Add", BindingFlags.NonPublic | BindingFlags.Instance);

            object[] sqlErrors = new object[errors.Length];
            for (int i = 0; i < errors.Length; i++)
            {
                sqlErrors[i] = GetError(errors[i].ErrorCode, errors[i].Message);
                addMethod.Invoke(collection, new object[] { sqlErrors[i] });

            }


            Type[] types = new Type[] { typeof(string), typeof(SqlErrorCollection) };

            object[] parameters = new object[] { mainMessage, collection }; 

            ConstructorInfo constructor = typeof(SqlException).
                GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, types, null);

            SqlException exception = (SqlException)constructor.Invoke(parameters);

            return exception;
        }

        private static SqlError GetError(int errorCode, string message)
        {
            object[] parameters = new object[] {
            errorCode, (byte)0, (byte)10, "server", message, "procedure", 0 };
            Type[] types = new Type[] { 
            typeof(int), typeof(byte), typeof(byte), typeof(string), typeof(string), 
            typeof(string), typeof(int) };

            ConstructorInfo constructor = typeof(SqlError).
                GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, types, null);
            SqlError error = (SqlError)constructor.Invoke(parameters);
            return error;
        }

        private static SqlErrorCollection GetErrorCollection()
        {
            ConstructorInfo constructor = typeof(SqlErrorCollection).
                GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null);
            SqlErrorCollection collection = (SqlErrorCollection)constructor.Invoke(new object[] { });
            return collection;
        }
    }

    public class Error
    {
        public int ErrorCode;
        public string Message;

        public Error(int errorCode, string message)
        {
            this.ErrorCode = errorCode;
            this.Message = message;
        }
    }
}